/**
* Copyright 2008-2009 Dan Pritchett
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.addsimplicity.anicetus.io;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import org.addsimplicity.anicetus.entity.EntityTypeRegistry;
import org.addsimplicity.anicetus.entity.GlobalInfo;
import org.addsimplicity.anicetus.entity.GlobalInfoFields;
import org.addsimplicity.anicetus.entity.JsonConstants;
import org.addsimplicity.anicetus.entity.TelemetryContainer;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
/**
* The encoder translates telemetry into a JSON string. The object graph is
* fully exported. In addition to serializing telemetry artifacts, the
* application can also set classes that are to be treated like beans when
* serializing. If an object is not a telemetry object and is not a bean, then
* its toString method is used to generate the value.
*
* @author Dan Pritchett (driveawedge@yahoo.com)
*
*/
public class JsonEncoder implements TelemetryEncoder {
private ExceptionHandler m_exceptionHandler = new SystemErrorExceptionHandler();
/**
* Encode the telemetry graph as JSON.
*
* @param session
* The telemetry graph to encode.
* @return the JSON string as a character array.
*/
public char[] encode(GlobalInfo session) {
CharArrayWriter out = new CharArrayWriter();
try {
JsonGenerator gen = (new JsonFactory()).createJsonGenerator(out);
writeEntity(session, gen, true);
gen.close();
out.close();
}
catch (IOException ioe) {
m_exceptionHandler.exceptionCaught(ioe);
}
return out.toCharArray();
}
/**
* Return the exception handler currently in effect.
*
* @return the exception handler.
*/
public ExceptionHandler getExceptionHandler() {
return m_exceptionHandler;
}
/**
* Set the exception handler that will receive exceptions encountered during
* encoding.
*
* @param exceptionHandler
* The exception handler.
*/
public void setExceptionHandler(ExceptionHandler exceptionHandler) {
m_exceptionHandler = exceptionHandler;
}
private void writeBean(Object value, JsonGenerator gen) throws JsonGenerationException, IOException {
gen.writeStartObject();
Class<? extends Object> cls = value.getClass();
gen.writeFieldName(JsonConstants.EntityType);
gen.writeString(cls.getSimpleName());
Method meths[] = cls.getMethods();
for (Method m : meths) {
String mname = m.getName();
if (mname.startsWith("get")) {
gen.writeFieldName(mname.substring(3));
try {
Object propVal = m.invoke(value);
writeValue(propVal, gen);
}
catch (IllegalArgumentException e) {
continue;
}
catch (IllegalAccessException e) {
continue;
}
catch (InvocationTargetException e) {
continue;
}
}
}
gen.writeEndObject();
}
private void writeEntity(GlobalInfo gi, JsonGenerator gen, boolean root) throws IOException, JsonGenerationException {
gen.writeStartObject();
gen.writeFieldName(JsonConstants.EntityType);
String type = EntityTypeRegistry.getShortName(gi);
if (type == null) {
type = gi.getClass().getSimpleName();
}
gen.writeString(type);
for (Map.Entry<String, Object> entry : gi.entrySet()) {
String propName = entry.getKey();
if (!root && propName.equals(GlobalInfoFields.ParentId.name())) {
// Skip the parent GUID as it's implicit in the containment structure.
continue;
}
String jsField = EntityTypeRegistry.getJsonKey(propName);
String fieldName = jsField != null ? jsField : propName;
gen.writeFieldName(fieldName);
writeValue(entry.getValue(), gen);
}
if (gi instanceof TelemetryContainer) {
TelemetryContainer cont = (TelemetryContainer) gi;
if (cont.getChildren().size() > 0) {
gen.writeFieldName("child");
gen.writeStartArray();
for (GlobalInfo g : cont.getChildren()) {
writeEntity(g, gen, false);
}
gen.writeEndArray();
}
}
gen.writeEndObject();
}
@SuppressWarnings("unchecked")
private void writeValue(Object value, JsonGenerator gen) throws JsonGenerationException, IOException {
if (value == null) {
gen.writeNull();
}
else if (EntityTypeRegistry.isBeanType(value.getClass())) {
writeBean(value, gen);
}
else if (value.getClass().isArray()) {
Object a[] = (Object[]) value;
gen.writeStartArray();
for (int i = 0; i < a.length; i++) {
writeValue(a[i], gen);
}
gen.writeEndArray();
}
else if (value instanceof Collection) {
Collection<? extends Object> c = (Collection<? extends Object>) value;
gen.writeStartArray();
for (Object v : c) {
writeValue(v, gen);
}
gen.writeEndArray();
}
else {
gen.writeString(value.toString());
}
}
}